package wang.jinggo.util;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.Date;
import java.util.Optional;

/**
 * 基于Java 8中引入全新的日期和时间API
 * @author wangyj
 * @description
 * @create 2017-06-22 8:22
 **/
public class DateUtils {

    private static WeekendQuery weekendQuery = new WeekendQuery();

    /**
     * 将Str 类型的(YYYY-MM-DD) 转换为LocalDate
     * @param dayStr
     * @return
     */
    public static LocalDate day(String dayStr) {
        return dayOpt(dayStr).orElse(null);
    }

    public static Optional<LocalDate> dayOpt(String dayStr) {
        if (StringUtils.isBlank(dayStr))
            return Optional.empty();
        return Optional.of(LocalDate.parse(dayStr.trim(), DateTimeFormatter.ISO_LOCAL_DATE));
    }

    /**
     * 将Str 类型的(YYYY-MM-DD) 转换为LocalDateTime
     * @param dateStr
     * @param formatter
     * @return
     */
    public static LocalDateTime date(String dateStr, DateTimeFormatter formatter) {
        return dateOpt(dateStr, formatter).orElse(null);
    }

    public static Optional<LocalDateTime> dateOpt(String dateStr, DateTimeFormatter formatter) {
        if (StringUtils.isBlank(dateStr))
            return Optional.empty();
        return Optional.of(LocalDateTime.parse(dateStr.trim(), formatter));
    }

    //传递过来是字符串的格式
    public static LocalDateTime date(String dateStr, String format) {
        return date(dateStr, DateTimeFormatter.ofPattern(format));
    }

    public static Optional<LocalDateTime> dateOpt(String dateStr, String format) {
        return dateOpt(dateStr, DateTimeFormatter.ofPattern(format));
    }

    //获取当天的开始时间 比如：2017-06-25:00:00:00
    public static LocalDateTime dateByDay(LocalDate day) {
        return dateByDayOpt(day).orElse(null);
    }
    public static Optional<LocalDateTime> dateByDayOpt(LocalDate day) {
        return Optional.ofNullable(day).map((v) -> v.atStartOfDay());
    }

    /** 获取当天的最后时间 比如：2017-06-25:59:59:59 */
    public static LocalDateTime dateTo(LocalDate day) {
        return dateToOpt(day).orElse(null);
    }

    public static Optional<LocalDateTime> dateToOpt(LocalDate day) {
        return Optional.ofNullable(day).map((v) -> v.atTime(23, 59, 59));
    }

    public static String dayFormat(LocalDate day) {
        return dayFormatOpt(day).orElse(null);
    }

    public static Optional<String> dayFormatOpt(LocalDate day) {
        return Optional.ofNullable(day).map((v) -> v.format(DateTimeFormatter.ISO_LOCAL_DATE));
    }

    /** 将指定日期类型的对象转换为字符串 */
    public static String dateFormat(LocalDateTime date, DateTimeFormatter formatter) {
        return dateFormatOpt(date, formatter).orElse(null);
    }

    public static Optional<String> dateFormatOpt(LocalDateTime date, DateTimeFormatter formatter) {
        return Optional.ofNullable(date).map((v) -> v.format(formatter));
    }

    /** 指定需要格式化的类型进行转换 */
    public static String dateFormat(LocalDateTime date, String format) {
        return dateFormatOpt(date, format).orElse(null);
    }

    public static Optional<String> dateFormatOpt(LocalDateTime date, String format) {
        return Optional.ofNullable(date).map((v) -> v.format(DateTimeFormatter.ofPattern(format)));
    }

    /** 日期之间的间隔 */
    public static Optional<Period> between(LocalDate start, LocalDate end) {
        if (start == null || end == null)
            return Optional.empty();
        return Optional.of(Period.between(start, end));
    }

    public static Optional<Duration> between(LocalDateTime start, LocalDateTime end) {
        if (start == null || end == null)
            return Optional.empty();
        return Optional.of(Duration.between(start, end));
    }

    /** 指定日期是否是周末 */
    public static boolean isWeekend(LocalDate day) {
        Assert.notNull(day);
        return day.query(weekendQuery);
    }

    /** 指定的年是有多少天 */
    public static LocalDate dayTo(int year) {
        return LocalDate.ofYearDay(year, Year.of(year).isLeap() ? 366 : 365);
    }

    //判断是否是周末
    public static class WeekendQuery implements TemporalQuery<Boolean> {

        @Override
        public Boolean queryFrom(TemporalAccessor temporal) {
            DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
            return dayOfWeek == DayOfWeek.SATURDAY  || dayOfWeek == DayOfWeek.SUNDAY;
        }
    }

    /**
     *Java 8中 java.util.Date 类新增了两个方法，分别是from(Instant instant)和toInstant()方法
     * 这两个方法使我们可以方便的实现将旧的日期类转换为新的日期类，具体思路都是通过Instant当中介，
     * 然后通过Instant来创建LocalDateTime（这个类可以很容易获取LocalDate和LocalTime），
     * 新的日期类转旧的也是如此，将新的先转成LocalDateTime，然后获取Instant，接着转成Date
     */

    //01.java.util.Date ---> java.time.LocalDateTime
    public static LocalDateTime dateToLocalDateTime(Date date){
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant,zone);
    }

    //02.java.util.Date ---> java.time.LocalDate
    public static LocalDate dateToLocalDate(Date date){
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalDate();
    }

    //03.java.util.Date ---> java.time.LocalTime
    public static LocalTime dateToLocalTime(Date date){
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalTime();
    }

    //04.java.time.LocalDateTime ---> java.util.Date
    public static Date localDateTimeToDate(LocalDateTime localDateTime){
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    //05.java.time.LocalDate ---> java.util.Date
    public static Date localDateToDate(LocalDate localDate){
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    //06.java.time.LocalTime ---> java.util.Date
    public static Date localTimeToDate(LocalDate localDate, LocalTime localTime){
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }
}
